/*
 * Copyright 2008. Mount Sinai Hospital, Toronto, Canada.
 * 
 * Licensed under the Apache License, Version 2.0. You
 * can find a copy of the license at:
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * IN NO EVENT SHALL MOUNT SINAI HOSPITAL BE LIABLE TO ANY PARTY FOR DIRECT, 
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 
 * PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, 
 * EVEN IF MOUNT SINAI HOSPITAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
 * DAMAGE.
 * 
 * MOUNT SINAI HOSPITAL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND 
 * ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". 
 * MOUNT SINAI HOSPITAL HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, 
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 
 */

package com.sinai.mshab.extensions.search;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.google.gwt.user.client.ui.SuggestionEvent;
import com.google.gwt.user.client.ui.SuggestionHandler;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sinai.mshab.format.Direction;
import com.sinai.mshab.format.FormatPanel;
import com.sinai.mshab.format.util.MshabEvent;
import com.sinai.mshab.format.util.Observable;
import com.sinai.mshab.format.util.Observer;

/**
 * A quick search widget that contains a single {@link SuggestBox}. The
 * {@link MultiWordSuggestOracle} is accessible and can thus be coupled with
 * server side requests. This widget will change to the
 * {@link MshabEvent#QUICK_SEARCH} status and notify observers when its button
 * is pressed. It will change to the {@link MshabEvent#QUICK_SEARCH_SUGGEST}
 * status on key presses and notify its observers.
 * 
 * @author Sergey Vesselov
 * @author Arthur Kalmenson
 */
public class QuickSearch extends VerticalPanel implements Observable {

	/**
	 * The suggest box used for searching.
	 */
	private SuggestBox suggestBox;

	/**
	 * The {@link SuggestOracle} we'll be populating with suggestions.
	 */
	private MultiWordSuggestOracle suggestOracle = new MultiWordSuggestOracle();

	/**
	 * The state of this {@code QuickSearch}.
	 */
	private MshabEvent state;

	/**
	 * The list of observers of this page.
	 */
	private List<Observer> observers;

	/**
	 * Creates a new {@code QuickSearch} with {@code suggestBoxLabel} in front
	 * of the {@link SuggestBox} (the instructions) and the text of the button,
	 * {@code buttonText}.
	 * 
	 * @param suggestBoxLabel
	 * @param buttonText
	 */
	public QuickSearch(String suggestBoxLabel, String buttonText) {

		// set up the panel
		FormatPanel panel = new FormatPanel();
		panel.setDefaultLabelPosition(Direction.TOP);

		// search textbox
		suggestBox = new SuggestBox(suggestOracle);
		suggestBox.addKeyboardListener(new SuggestListener());
		suggestBox.addEventHandler(new SuggestChosenListener());

		panel.add(suggestBox);
		panel.setLabel(suggestBox, suggestBoxLabel);

		// search button
		Button search = new Button(buttonText);
		panel.add(search);
		search.addClickListener(new SearchListener());

		add(panel);
	}

	/**
	 * Returns the {@link SuggestOracle} that this {@code QuickSearch} is using.
	 * 
	 * @return the {@link SuggestOracle} that this {@code QuickSearch} is using.
	 */
	public MultiWordSuggestOracle getSuggestOracle() {
		return suggestOracle;
	}

	/**
	 * Returns the test that this {@code QuickSearch}'s {@link SuggestBox}
	 * contains.
	 * 
	 * @return the test that this {@code QuickSearch}'s {@code SuggestBox}
	 *         contains.
	 */
	public String getText() {
		return suggestBox.getText();
	}

	public void addObserver(Observer o) {

		// initialize if required.
		if (observers == null) {
			observers = new ArrayList<Observer>();
		}

		observers.add(o);
	}

	public void notifyObservers() {

		// notify all of our observers.
		for (Observer observer : observers) {
			observer.update(this, state);
		}
	}

	public int countObservers() {
		return observers.size();
	}

	public void deleteObserver(Observer observer) {
		observers.remove(observer);
	}

	public void deleteObservers() {
		observers.clear();
	}
	
	/**
	 * The {@link ClickListener} that will change this {@code QuickSearch}'s
	 * state to {@link MshabEvent#QUICK_SEARCH} and notify observers.
	 * 
	 * @author Arthur Kalmenson
	 */
	private class SearchListener implements ClickListener {

		public void onClick(Widget sender) {
			state = MshabEvent.QUICK_SEARCH;
			notifyObservers();
		}
	}

	/**
	 * The {@link KeyboardListener} that will catch key presses and change the
	 * state to {@link MshabEvent#QUICK_SEARCH_SUGGEST} and notify observers.
	 * 
	 * @author Arthur Kalmenson
	 */
	private class SuggestListener implements KeyboardListener {

		public void onKeyDown(Widget sender, char keyCode, int modifiers) {
			// do nothing
		}

		public void onKeyPress(Widget sender, char keyCode, int modifiers) {

			// notify our observer that we've had a key press.
			state = MshabEvent.QUICK_SEARCH_SUGGEST;
			notifyObservers();
		}

		public void onKeyUp(Widget sender, char keyCode, int modifiers) {
			// do nothing
		}
	}

	/**
	 * The {@link SuggestionHandler} that will catch when a suggestion is
	 * selected and change the state to
	 * {@link MshabEvent#QUICK_SEARCH_SUGGEST_CHOSEN} and notify the current
	 * observers.
	 * 
	 * @author Arthur Kalmenson
	 */
	private class SuggestChosenListener implements SuggestionHandler {
		public void onSuggestionSelected(SuggestionEvent event) {
			state = MshabEvent.QUICK_SEARCH_SUGGEST_CHOSEN;
			notifyObservers();
		}
	}
}
